#pragma once
#include <queue>
#include <iostream>
#include <pthread.h>
#include "Mutex.hpp"
#include "cond.hpp"

using namespace MutexModule;
using namespace CondModule;

const int defaultcap = 5;

template <class T>
class BlockQueue
{
public:
    BlockQueue(int cap = defaultcap)
        : _capacity(cap), _csleep_num(0), _psleep_num(0)
    {
    }

    ~BlockQueue()
    {
    }

    void Equeue(const T &data)
    {
        {
            // 生产者生产
            MutexGuard guard(_mutex);
            while (IsFull()) // 所以这里用while增强健壮性，让其重复检查，以免满的时候push
            {
                _psleep_num++;
                // wait函数有可能会失败，失败后立即返回，但此时队列是满的，再push数据导致错误
                // wait可能会因为条件不满足而被唤醒，叫伪唤醒
                _fullcond.Wait(_mutex);
                _psleep_num--;
            }

            _q.push(data);

            if (_csleep_num > 0)
            {
                _emptycond.Signal();
                std::cout << "唤醒消费者...." << std::endl;
            }
        }
    }

    T Pop()
    {
        T data;
        {
            // 消费者消费
            MutexGuard guard(_mutex);

            while (IsEmpty())
            {
                _csleep_num++;
                _emptycond.Wait(_mutex);
                _csleep_num--;
            }

            data = _q.front();
            _q.pop();

            if (_psleep_num > 0)
            {
                _fullcond.Signal();
                std::cout << "唤醒生产者...." << std::endl;
            }
        }

        return data;
    }

private:
    std::queue<T> _q;
    Mutex _mutex;
    Cond _emptycond;
    Cond _fullcond;
    int _capacity;
    int _csleep_num;
    int _psleep_num;

    bool IsEmpty()
    {
        return _q.empty();
    }

    bool IsFull()
    {
        return _q.size() >= _capacity;
    }
};